package com.yh.csx.business.api.exception;

import com.yh.csx.bsf.core.base.BsfLevel;
import com.yh.csx.bsf.core.thread.ThreadPool;
import com.yh.csx.bsf.core.util.ExceptionUtils;
import com.yh.csx.bsf.health.base.AbstractCollectTask;
import com.yh.csx.bsf.health.base.EnumWarnType;
import com.yh.csx.bsf.health.base.Message;
import com.yh.csx.business.api.enums.BizError;
import lombok.extern.slf4j.Slf4j;

import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;

/**
 * 业务错误定义
 */
@Slf4j
public class BusinessException extends RuntimeException {

    private static final long serialVersionUID = -3942998119484283865L;

    private static final String PACKAGE_PREFIX;

    static {
        String[] paths = BusinessException.class.getPackage().getName().split("\\.");
        PACKAGE_PREFIX = paths[0] + "." + paths[1];
    }

    private int code;
    private ExceptionDetails details;

    public int getCode() {
        return code;
    }

    public ExceptionDetails getDetails() {
        return details;
    }

    private BusinessException(){}

    public BusinessException(ExceptionDetails details) {
        super(details.getMessage());
        this.code = details.getCode();
        this.details=details;
        reportException(details,this);
    }

    public BusinessException(ExceptionDetails details,Throwable cause) {
        super(details.getMessage());
        this.code = details.getCode();
        this.details=details;
        reportException(details,cause);
    }

    @Deprecated
    public BusinessException(int code, String message, Throwable cause) {
        super(message, cause);
        this.code = code;
        details=ExceptionDetails.createBuilder(code,message).build();
        reportException(details);
    }
    @Deprecated
    public BusinessException(int code, String message) {
        super(message);
        this.code = code;
        details=ExceptionDetails.createBuilder(code,message).build();
        reportException(details);
    }
    @Deprecated
    public BusinessException(int code, Throwable cause) {
        super(cause);
        this.code = code;
        details=ExceptionDetails.createBuilder(code,getMessage()).build();
        reportException(details);
    }
    @Deprecated
    public BusinessException(String message, Throwable cause) {
        super(message, cause);
        this.code = BizError.COMMON_ERROR.getCode();
        details=ExceptionDetails.createBuilder(code,message).build();
        reportException(details);
    }
    @Deprecated
    public BusinessException(String message) {
        super(message);
        this.code = BizError.COMMON_ERROR.getCode();
        details=ExceptionDetails.createBuilder(code,message).build();
        reportException(details);
    }
    @Deprecated
    public BusinessException(BizError error, Throwable cause) {
        this(error.getCode(), error.getMessage(), cause);
        details=ExceptionDetails.createBuilder(error.getCode(), error.getMessage()).build();
        reportException(details);
    }

    @Deprecated
    public BusinessException(BizError error) {
        this(error.getCode(), error.getMessage());
        details=ExceptionDetails.createBuilder(error.getCode(), error.getMessage()).build();
        reportException(details,this);
    }

    private void reportException(ExceptionDetails details){
        reportException(details,this);
    }

    private void reportException(ExceptionDetails details,Throwable throwable){
        if (details.getCode() <= 0 || details.getCode() == BizError.COMMON_ERROR.getCode() || details.getCode() == BizError.SYSTEM_ERROR.getCode()) {
            ThreadPool.System.submit("TASK", () ->{
                Message message=new Message();
                message.setTitle("异常");
                message.setExceptionCode(String.valueOf(details.getCode()));
                message.setWarnType(EnumWarnType.ERROR);
                AtomicReference<String> outputParams= new AtomicReference(details.getMessage());
                Optional.ofNullable(details.getBizLevel()).ifPresent(v->message.setLevelType(BsfLevel.resolve(v)));
                Optional.ofNullable(details.getBizScope()).ifPresent(v->message.setBizScope(v));
                Optional.ofNullable(details.getBizDetails()).ifPresent(v-> outputParams.set(outputParams.get()+"\nbizDetails:"+ v));
                Optional.ofNullable(throwable).ifPresent(v-> outputParams.set(outputParams.get()+"\nFullStackTrace:"+ ExceptionUtils.getFullStackTrace(v)));
                message.setContent(outputParams.get());
                AbstractCollectTask.notifyMessage(message);
            });
        }
    }
    @Override
    /**
     * 过滤非业务错误的堆栈信息
     */
    public synchronized Throwable fillInStackTrace() {
        Throwable throwable = super.fillInStackTrace();
        StackTraceElement[] stack = super.getStackTrace();
        if (stack == null) {
            return throwable;
        }
        int count = 0;
        for (int i = stack.length - 1; i >= 0; i--) {
            StackTraceElement e = stack[i];
            if (!e.getClassName().startsWith(PACKAGE_PREFIX) || e.getLineNumber() < 0) {
                stack[i] = null;
            } else {
                count++;
            }
        }
        if (count != stack.length) {
            StackTraceElement[] temp = new StackTraceElement[count];
            int index = 0;
            for (StackTraceElement e : stack) {
                if (e == null) {
                    continue;
                }
                temp[index++] = e;
            }
            stack = temp;
        }
        setStackTrace(stack);
        return throwable;
    }
}
